/**
 * Copyright 2011 Joe Choi, All rights reserved.
 */
package tao.calendar.gregorian;

import java.util.Calendar;
import tao.calendar.gregorian.Gregorian.Unit;
import tao.time.Time;

/**
 * The ConcreteOperatableDateTime is the concrete implementation of OperatableDateTime
 * interface 
 * 
 * @author Joe Choi
 * @version 1.0.0
 */
class ConcreteOperatableDateTime extends ConcreteGregorianDateTime implements OperatableDateTime {
	public ConcreteOperatableDateTime(GregorianDateTime dt) {
		super(dt);
	}

	@Override
	public Operatable roll(GregorianUnitAmount amount) {
		OperatableDate date = new ConcreteOperatableDate(
								new ConcreteGregorianDate(getEra(), getYear(), getMonth(), getDay()));
		OperatableTime time = new ConcreteOperatableTime(
								new ConcreteGregorianTime(getHour(), getMinute(), getSecond(), getMillisecond()));
		switch(amount.getUnit()) {
			case Year: case Month: case Day: date.roll(amount); break;
			case Hour: case Minute: case Second: case Millisecond: time.roll(amount); break;
		}
		set(date, time);
		return(this);
	}
	
	@Override
	public Operatable add(GregorianUnitAmount amount) {
		Calendar c = getCalendar();
		c.setTimeInMillis(scalar());
		
		switch(amount.getUnit()) {
			case Year : c.add(Calendar.YEAR, (int) amount.getValue()); break; 
			case Month : c.add(Calendar.MONTH, (int) amount.getValue()); break; 
			case Day : c.add(Calendar.DAY_OF_MONTH, (int) amount.getValue()); break;
			case Hour : c.add(Calendar.HOUR_OF_DAY, (int) amount.getValue()); break;
			case Minute : c.add(Calendar.MINUTE, (int) amount.getValue()); break;
			case Second : c.add(Calendar.SECOND, (int) amount.getValue()); break;
			case Millisecond : c.add(Calendar.MILLISECOND, (int) amount.getValue()); break;
		}
		set(c);
		return(this);
	}

	@Override
	public GregorianAmount diff(Time other) {
		return(constructAmount(Math.abs(this.scalar() - other.scalar())));
	}
	
	private GregorianAmount constructAmount(long scalar) {
		GregorianAmount amount = null;
		
		amount = ConcreteOperatableTime.constructTimeAmount(scalar % GregorianTime.DAY_MILLIS);
		amount = amount.getModifiable().
					setValue(Unit.Day, (int)(scalar / GregorianTime.DAY_MILLIS)).getAmount();
		return(amount);
	}	
}
